//---------------------------------------------------------------------------------
// Copyright (c) 2011, Microsoft Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//---------------------------------------------------------------------------------

namespace PolicyPublisher
{
    using System;
    using System.Collections.Specialized;
    using System.IO;
    using System.Linq;
    using System.Net;
    using System.Text;

    class Program
    {
        private const string PublishAction = "publish";
        private const string DeleteAction = "delete";

        private const string ServiceBusUriPostFix = "servicebus.windows.net";
        private const string AccessControlUriPostFix = "accesscontrol.windows.net";

        private const string AuthorizationHeader = "Authorization";

        private enum Action
        {
            Publish,
            Delete
        }

        static void Main(string[] args)
        {
            Console.WriteLine("Please enter the following info for uploading a clientaccesspolicy file:");

            Console.Write("\nService Namespace: ");
            string serviceNamespace = Console.ReadLine();

            Console.Write("\nIssuer Name: ");
            string issuerName = Console.ReadLine();

            Console.Write("\nIssuer Key: ");
            string issuerKey = Console.ReadLine();

            Console.Write("\nPublish/Delete Policy file (publish/delete): ");
            string action = Console.ReadLine();

            Action actionForPolicyFile;

            string policyFile;
            byte[] fileContentArray = null;

            if (action.Equals(PublishAction, StringComparison.OrdinalIgnoreCase))
            {
                actionForPolicyFile = Action.Publish;

                Console.Write("\nPolicy File Location: ");
                policyFile = Console.ReadLine();

                if (File.Exists(policyFile))
                {
                    fileContentArray = File.ReadAllBytes(policyFile);
                }
                else
                {
                    Console.Write("\nSpecified policy File " + policyFile + " does not exist");
                    return;
                }
            }
            else if (action.Equals(DeleteAction, StringComparison.OrdinalIgnoreCase))
            {
                actionForPolicyFile = Action.Delete;
            }
            else
            {
                Console.Write("\nInvalid option for publish/delete policy file");
                return;
            }

            string rootUri = string.Format("http://{0}.{1}/", serviceNamespace, ServiceBusUriPostFix);

            string formattedToken = string.Format("WRAP access_token=\"{0}\"", Program.GetToken(rootUri, serviceNamespace, issuerName ,issuerKey));

            if (actionForPolicyFile == Action.Publish)
            {
                // Publish clientaccesspolicy.xml at the root of the namespace            
                Program.PublishClientAccessPolicy(serviceNamespace, formattedToken, fileContentArray);
                Console.WriteLine("Client Access policy published.");
            }
            else
            {
                // Delete clientaccesspolicy.xml from the root of the namespace
                Program.DeleteClientAccessPolicy(serviceNamespace, formattedToken);
                Console.WriteLine("Client Access policy deleted.");
            }
        }

        static string GetToken(string messageBufferLocation, string serviceNamespace, string issuerName, string issuerKey)
        {
            using (WebClient client = new WebClient())
            {
                client.BaseAddress = string.Format("https://{0}-sb.{1}/", serviceNamespace, AccessControlUriPostFix);
                NameValueCollection values = new NameValueCollection();
                values.Add("wrap_name", issuerName);
                values.Add("wrap_password", issuerKey);
                values.Add("wrap_scope", messageBufferLocation);

                Console.WriteLine("Requesting token from ACS");

                byte[] responseBytes = client.UploadValues("WRAPv0.9", "POST", values);
                string response = Encoding.UTF8.GetString(responseBytes);

                // A typical response body (with fields blanked for security): wrap_access_token=[claim type]%3d[claim value]%26Issuer%3d[service namespace]%26Audience%3d[audience scope]%26ExpiresOn%3d[token policy expires on]%26HMACSHA256%3d[signature]&wrap_access_token_expires_in=[some int]
                // Get the wrap_access_token only:
                return Uri.UnescapeDataString(response.Split('&').Single(value => value.StartsWith("wrap_access_token=", StringComparison.OrdinalIgnoreCase)).Split('=')[1]);
            }
        }

        static HttpStatusCode PublishClientAccessPolicy(string serviceNamespace, string authHeaderValue, byte[] fileContentArray)
        {
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(string.Format("https://{0}.{1}/clientaccesspolicy.xml", serviceNamespace, ServiceBusUriPostFix));

            request.Method = "PUT";
            request.ContentType = "text/xml";

            request.ContentLength = fileContentArray.Length;
            request.Headers[AuthorizationHeader] = authHeaderValue;

            Stream dataStream = request.GetRequestStream();
            dataStream.Write(fileContentArray, 0, fileContentArray.Length);
            dataStream.Close();

            return SendHttpRequestAndGetResponse(request);
        }

        static HttpStatusCode DeleteClientAccessPolicy(string serviceNamespace, string authHeaderValue)
        {
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(string.Format("https://{0}.{1}/clientaccesspolicy.xml", serviceNamespace, ServiceBusUriPostFix));

            request.Method = "DELETE";
            request.Headers[AuthorizationHeader] = authHeaderValue;

            return SendHttpRequestAndGetResponse(request);
        }

        static HttpStatusCode SendHttpRequestAndGetResponse(HttpWebRequest request)
        {
            HttpWebResponse response = null;
            HttpStatusCode responseStatusCode;

            try
            {
                response = (HttpWebResponse)request.GetResponse();
                responseStatusCode = response.StatusCode;
            }
            catch (WebException exception)
            {
                Program.HandleWebException(exception);
                throw exception;
            }
            finally
            {
                if (response != null)
                {
                    response.Close();
                }
            }

            return responseStatusCode;
        }

        static void HandleWebException(WebException e)
        {
            if (e != null && e.Response != null)
            {
                if (e.Response.GetResponseStream() != null)
                {
                    Console.WriteLine("\nEncountered exception: {0}\n", e.Message);
                    using (HttpWebResponse httpWebResponse = e.Response as HttpWebResponse)
                    {
                        using (StreamReader reader = new StreamReader(httpWebResponse.GetResponseStream()))
                        {
                            Console.WriteLine(httpWebResponse.StatusCode);
                            Console.WriteLine(reader.ReadToEnd());
                        }
                    }
                }

                e.Response.Close();
            }
        }
    }
}
